package set

import (
	"gitee.com/Billcoding/gotypes"
	"sync"
)

//define HashSet struct
type HashSet struct {
	//elements
	elements map[gotypes.Element]gotypes.Element
	//a sync locker
	mutex sync.Mutex
}

//new HashSet
func NewHashSet() *HashSet {
	return &HashSet{
		elements: make(map[gotypes.Element]gotypes.Element),
		mutex:    sync.Mutex{},
	}
}

//Clear set
func (hashSet *HashSet) Clear() {
	hashSet.mutex.Lock()
	defer hashSet.mutex.Unlock()
	hashSet.elements = make(map[gotypes.Element]gotypes.Element)
}

//return set size
func (hashSet *HashSet) Size() int {
	return len(hashSet.elements)
}

//add element
func (hashSet *HashSet) Add(e gotypes.Element) *HashSet {
	hashSet.mutex.Lock()
	defer hashSet.mutex.Unlock()
	hashSet.elements[e] = 0
	return hashSet
}

//remove element
func (hashSet *HashSet) Remove(e gotypes.Element) {
	hashSet.mutex.Lock()
	defer hashSet.mutex.Unlock()
	delete(hashSet.elements, &e)
}

//check e contains
func (hashSet *HashSet) Contains(e gotypes.Element) bool {
	return hashSet.elements[e] != nil
}

//foreach function
func (hashSet *HashSet) ForEach(fn func(e gotypes.Element)) {
	for k := range hashSet.elements {
		fn(k)
	}
}

//map function
func (hashSet *HashSet) Map(mapFn func(e gotypes.Element) gotypes.Element) *HashSet {
	return hashSet.fn(func(e gotypes.Element) bool {
		return true
	}, mapFn)
}

//filter function
func (hashSet *HashSet) Filter(filterFn func(e gotypes.Element) bool) *HashSet {
	return hashSet.fn(filterFn, func(e gotypes.Element) gotypes.Element {
		return e
	})
}

//reduce function
func (hashSet *HashSet) Reduce(val gotypes.Element, reduceFn func(val gotypes.Element, e gotypes.Element) gotypes.Element) gotypes.Element {
	valPtr := &val
	hashSet.ForEach(func(e gotypes.Element) {
		*valPtr = reduceFn(*valPtr, e)
	})
	return *valPtr
}

//matchAny function
func (hashSet *HashSet) MatchAny(matchAnyFn func(e gotypes.Element) bool) bool {
	for k := range hashSet.elements {
		if matchAnyFn(k) {
			return true
		}
	}
	return false
}

//matchNone function
func (hashSet *HashSet) MatchNone(matchNoneFn func(e gotypes.Element) bool) bool {
	for k := range hashSet.elements {
		if matchNoneFn(k) {
			continue
		}
		return true
	}
	return false
}

//matchAll function
func (hashSet *HashSet) MatchAll(matchAllFn func(e gotypes.Element) bool) bool {
	count := 0
	for k := range hashSet.elements {
		if matchAllFn(k) {
			count++
		}
	}
	return count == hashSet.Size()
}

func (hashSet *HashSet) fn(filterFn func(e gotypes.Element) bool, mapFn func(e gotypes.Element) gotypes.Element) *HashSet {
	newHashSet := NewHashSet()
	for k := range hashSet.elements {
		if filterFn(k) {
			newHashSet.Add(mapFn(k))
		}
	}
	return newHashSet
}
